from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
from sklearn.metrics import accuracy_score
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation
from tensorflow.keras import optimizers
from tensorflow.keras import regularizers
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import seaborn as sns
from tqdm import tqdm


def threshold(x, d):                               #`硬阈值函数`
    return [1 if xi>d else 0 for xi in x]

def fit(x,y,LearningRate,epoches,w,b):             #`训练函数`
    epochs = []
    train_accuracy = []
    test_accuracy = []
    for step in tqdm(range(epoches)):
        for i in range(x.shape[0]):
            h= threshold(np.dot(w,x[i])+b,0)
            w=w+LearningRate*(y[i]-h)*x[i]         #`感知机规则`
            b=b+LearningRate*(y[i]-h)
        pred_train = predict(x_train, w, b)
        pred_test = predict(x_test, w, b)
        train_accuracy.append(accuracy_score(y_train, pred_train))
        test_accuracy.append(accuracy_score(y_test, pred_test))
        epochs.append(step)
    draw(epochs, test_accuracy, 'Epoch', 'Cost', "硬阈值线性分类中test_accuracy随epoch变化的图像")
    draw(epochs, train_accuracy, 'Epoch', 'Cost', "硬阈值线性分类中train_accuracy随epoch变化的图像")
    return w,b

def predict(x, w, b):                                    #`分类函数`
   return threshold(np.dot(x, w)+b, 0)

breast_cancer = datasets.load_breast_cancer()
x_train, x_test, y_train, y_test = train_test_split(
    breast_cancer.data,
    breast_cancer.target,
    test_size=0.3,
    random_state=420)

def hard_threshold():
    w = np.random.random(x_train.shape[1])  # `w,b随机初始化`
    b = np.random.random(1)
    # 原始的学习率 0.001
    w, b = fit(x_train, y_train, 0.005, 2000, w, b)  # `训练w,b`

    # 打印w和b
    print('Weights=', w, '\nbiases=', b)
    print(type(w))
    draw2(range(len(w)),w)
    find_largest_deviation(w)

    pred_train = predict(x_train, w, b)
    pred_test = predict(x_test,  w, b)
    print(accuracy_score(y_train, pred_train))
    print(accuracy_score(y_test, pred_test))

#`以上是硬阈值线性分类，以下是logistic线性分类`
def linear_classification():
    model = Sequential()
    model.add(Dense(input_dim=x_train.shape[1],
                    units=1,
                    activation='sigmoid',
                    kernel_regularizer=regularizers.l1(0.2)))

    op = optimizers.RMSprop(learning_rate=0.0001)
    model.compile(loss='mse', optimizer=op)
    epochs = []
    costs = []
    test_accuracy = []
    train_accuracy = []
    for epoch in tqdm(range(20000), ncols=100):
        cost = model.train_on_batch(x_train, y_train)
        # 画出epoch和cost的关系图
        # costs.append(cost)
        # epochs.append(epoch)
        # pred_train = threshold(model.predict(x_train), 0.5)
        # pred_test = threshold(model.predict(x_test), 0.5)
        # test_accuracy.append(accuracy_score(y_test, pred_test))
        # train_accuracy.append(accuracy_score(y_train, pred_train))
        if epoch % 1000 == 0:
            print("epoch %d , cost: %f" % (epoch, cost))

    w, b = model.layers[0].get_weights()

    print('Weights=', w, '\nbiases=', b)

    real_w = [t_w[0] for t_w in w]
    real_w=np.array(real_w)

    draw2(range(len(real_w)), real_w)
    find_largest_deviation(real_w)

    pred_train = threshold(model.predict(x_train), 0.5)
    pred_test = threshold(model.predict(x_test), 0.5)
    print(accuracy_score(y_train, pred_train))
    print(accuracy_score(y_test, pred_test))
    # draw(epochs, costs, 'Epoch', 'Cost', "logistic线性分类中cost随epoch变化的图像")
    # draw(epochs, test_accuracy, 'Epoch', 'Cost', "logistic线性分类中test_accuracy随epoch变化的图像")
    # draw(epochs, train_accuracy, 'Epoch', 'Cost', "logistic线性分类中train_accuracy随epoch变化的图像")

def draw(x, y, x_label, y_label, title):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    sns.set(font='SimHei')
    plt.plot(x, y)
    plt.title(title, fontproperties=FontProperties(fname=None, size=14), loc='center')
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.show()

def draw2(x,y):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    sns.set(font='SimHei')
    plt.plot(x, y,"*")
    plt.plot(x, y)
    plt.title("各特征值的w", loc='center')
    plt.xlabel("特征点")
    plt.ylabel("特征点的w")
    plt.show()

def find_largest_deviation(array, n=4):
    # 找到偏离0较大的前n个值及其索引
    absolute_values = np.abs(array)
    indices = np.argsort(absolute_values)[-n:]
    values = array[indices]

    print("偏离0较大的特征点的下标", indices)
    print("偏离0较大的特征点的w", values)



if __name__ == '__main__':
    # 硬阈值分类
    # hard_threshold()

    # 线性分类
    linear_classification()